#version 440 core

#define LOC_SIZE 64

#define GRID_SIZE_X 64
#define GRID_SIZE_Y 64

uniform uint numFinishedNodes;

layout(local_size_x=LOC_SIZE) in;

layout(binding=0, r32ui) uniform coherent uimage2D levelSetStateImage;
layout(binding=1, r32f) uniform readonly image2D levelSetImageSrc;
layout(binding=2, r32f) uniform writeonly image2D levelSetImageDst;

layout(binding=0, offset=0) uniform atomic_uint activeNodesCounter;

layout(std430, binding=0) buffer finishedNodesBuffer
{
	ivec2 finishedNodes[];
};

layout(std430, binding=1) buffer activeNodesBuffer
{
	ivec2 activeNodes[];
};

void main(void)
{
	uint gid = gl_GlobalInvocationID.x;
	if (gid < numFinishedNodes)
	{
		ivec2 nodePosition = finishedNodes[gid];
		
		imageStore(levelSetImageDst, nodePosition,
			vec4(imageLoad(levelSetImageSrc, nodePosition).x,0.0,0.0,1.0));
		
		ivec2 neighborPositions[4] = {
			clamp(nodePosition+ivec2(-1,0), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y)),
			clamp(nodePosition+ivec2(1,0), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y)),
			clamp(nodePosition+ivec2(0,-1), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y)),
			clamp(nodePosition+ivec2(0,1), ivec2(0,0), ivec2(GRID_SIZE_X, GRID_SIZE_Y))
		};
		
		for (uint i=0; i<4; i++)
		{
			if (imageAtomicExchange(levelSetStateImage, neighborPositions[i], 1u) == 0)
			{
				uint index = atomicCounterIncrement(activeNodesCounter);
				activeNodes[index] = neighborPositions[i];
			}
		}
	}
}